#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/fmt/ranges.h"
#include "spdlog/spdlog.h"
#include "thread_pool.h"
#include "topo.h"
#include <map>
#include <algorithm>
#include <cstdio>
#include <fstream>
#include <httplib.h>
#include <iostream>
#include <limits>
#include <signal.h>
#include <string>
#include <sw/redis++/redis++.h>
#include <thread>
#include <unistd.h>
#include <unordered_map>
#include <vector>

using namespace std;

std::unordered_map<std::string, std::pair<std::string, int>> disDictionary;

Topo topo;
sw::redis::Redis redis_cli(REDIS_MEASURE);
ThreadPool pool(THREAD_NUMS);

bool sigflag = false;

/*
 * 每秒钟检测一次所有业务路由的变化情况，
 * 根据路由安排的变化，通过ONOS下发对应的流表到各个卫星！
 * 采用线程池来提高给ONOS发送流表请求的速率！
 */
void execute_script(int signo)
{
    sigflag = true;
}

/*
 * 交互线程，用于中途添加、删除业务流
 */
void *interactive_thread(void *f)
{

    sigset_t set;
    int cmd, cmd2, err, tos, is_dis, bind, agree_tos, delay_need;
    string src, dst, tmd, path;

    sigaddset(&set, SIGALRM);
    err = sigprocmask(SIG_BLOCK, &set, NULL);
    if (err != 0)
    {
        spdlog::error("设置信号屏蔽失败");
        exit(EXIT_FAILURE);
    }

    while (topo.m_slot <= MAX_SLOT)
    {
        cout << "输入指令ID [0: 添加业务], [1: 删除业务], [-1: 退出]  > ";
        cin >> cmd;
        switch (cmd)
        {
        case 0:
            cout << "输入新增业务信息 [0: 普通业务], [1: 高可靠业务], [2: 时敏业务], [-1: 退出]> ";
            cin >> cmd2;
            switch (cmd2)
            {
            case 0:
                cout << "输入新增普通业务信息【起始节点 目的节点 TOS】> ";
                cin >> src >> dst >> tos;
                topo.add_flow(src, dst, tos, 0, 0, 0);
                cout << "成功下发普通业务 " << endl;
                break;
            case 1:
                cout << "输入新增高可靠业务信息【起始节点 目的节点 TOS】> ";
                cin >> src >> dst >> tos;
                topo.add_flow(src, dst, tos, 1, 0, 0);
                cout << "成功下发高可靠业务 " << endl;
                break;
            case 2:
                cout << "输入新增 时敏业务信息【起始节点 目的节点 TOS 时敏业务带宽需求(n or 0) 是否接受降级(1 or 0) 时延需求(n or 0)】> ";
                cin >> src >> dst >> tos >> bind >> agree_tos >> delay_need;
                if (bind == 0 && delay_need == 0)
                {
                    topo.add_flow(src, dst, tos, 0, 0, 0);
                    cout << "无带宽和时延需求，成功下发时敏业务 " << endl;
                }
                else
                {
                    tmd = ROUTING_TOS_PATH_CHANGE;
                    path = format(tmd, src, dst, tos, bind);
                    auto res = topo.http_cli.Post(path);
                    if (!res)
                    {
                        spdlog::error("请求路由失败");
                    }

                    if (res->status != 200)
                    {
                        spdlog::warn(" 请求路由异常");
                    }
                    int flag_b = 0;
                    int flag_d = 0;
                    json route_main;
                    route_main = json::parse(res->body);
                    spdlog::info("Response body: {}", res->body);
                    // spdlog::info("Parsed JSON: {}", route_main.dump());

                    for (auto &item : route_main.items())
                    {
                        // 对每个键值对进行处理
                        std::string key = item.key();
                        json value = item.value();
                        string arr = value["flag_demand_width"];
                        string delay_max = value["maxDelay"];
                        float delay_max_f = std::stof(delay_max);

                        if (arr == "0")
                        {
                            spdlog::info("key flag_b {}", key);
                            flag_b = 1;
                        }
                        if (delay_max_f > delay_need && delay_need != 0)
                        {
                            spdlog::info("key flag_d {}", key);
                            flag_d = 1;
                        }
                    }
                    spdlog::warn(" flag_demand_width正常 flag_b={} flag_d = {}", flag_b, flag_d);
                    // 说明链路满足带宽需求
                    if (flag_d == 1)
                    {
                        cout << "时延需求无法满足，下发时敏业务失败（暂不考虑带宽需求）" << endl;
                    }
                    else if (flag_b == 0)
                    {
                        cout << "时延需求满足，带宽需求满足，成功下发时敏业务，无需降级 " << endl;
                        topo.add_flow(src, dst, tos, 0, 1, 0);
                    }
                    // 链路不满足带宽需求，且同意降级
                    else if (agree_tos == 1)
                    {

                        cout << "时延需求满足，带宽需求不满足，但同意降级，成功下发时敏业务,降级为tos=7 " << endl;
                        topo.add_flow(src, dst, tos, 0, 2, bind);
                    }
                    // 链路不满足带宽需求，但不同意降级
                    else
                    {
                        cout << "时延需求满足，带宽需求不满足且不同意降级，下发时敏业务失败" << endl;
                    }
                }
            case -1:
                break;
            default:
                cout << "输入错误，请重新输入" << endl;
                cin.clear();
                cin.ignore(numeric_limits<streamsize>::max(), '\n');
            }
            break;
        case 1:
            cout << "输入删除业务信息【起始节点 目的节点 TOS 是否分离】> " << endl;
            cin >> src >> dst >> tos >> is_dis;
            topo.del_flow(src, dst, tos, is_dis);
            break;
        case -1:
            pthread_exit(NULL);
        default:
            cout << "输入错误，请重新输入" << endl;
            cin.clear();
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
        }
    }
    return NULL;
}

static void
dynamic_routing(Flow *fp)
{
    string path;
    int retry_cnt = 0;

retry:
    path = format(DYNAMIC_ROUTING, fp->src, fp->dst, fp->slice, fp->tos);
    spdlog::debug("请求动态路由，path: {}", path);

    auto res = topo.http_cli.Post(path);
    if (!res)
    {
        spdlog::error("flow: {} 请求动态路由失败：{}", fp->id, to_string(res.error()));
        return;
    }
    else if (res->status == 200 && res->body.size() > 30)
    {
        spdlog::debug("flow: {} 请求动态路由成功！", fp->id);
    }
    else
    {
        spdlog::debug("flow: {} 请求动态路由失败, body: {}", fp->id, res->body);
        sleep(1);
        if (retry_cnt < 3)
        {
            retry_cnt++;
            goto retry;
        }
        else
        {
            spdlog::info("连续3次请求动态失败，path: {}", path);
            return;
        }
    }

    // 解析路由信息，可能有多条流
    auto flows = json::parse(res->body);
    // logfile << format("dynamic route: {}\n", res->body); // debug 日志
    // logfile.flush();
    pthread_rwlock_wrlock(&topo.flows_rwlock);
    for (auto &flow : flows.items())
    {
        string flow_id = flow.key();
        spdlog::info("为 flow: {} 分配了新路由", flow_id);

        if (topo.flows.find(flow_id) == topo.flows.end())
        {
            spdlog::info("flow_id: {} not exists!", flow_id);
            continue;
        }

        // spdlog::info("old_path: {}", topo.flows[flow_id].route[topo.m_slice]["path"]);
        pthread_mutex_lock(&topo.flows[flow_id].flow_mutex);

        spdlog::info("old_path: {}",
                     fmt::join(topo.flows[flow_id].route[topo.m_slice]["path"], ","));
        topo.flows[flow_id].route = flow.value();
        spdlog::info("new_path: {}",
                     fmt::join(topo.flows[flow_id].route[topo.m_slice]["path"], ","));

        topo.flows[flow_id].cnt = 0;
        topo.flows[flow_id].update_rule = true;

        pthread_mutex_unlock(&topo.flows[flow_id].flow_mutex);
    }
    pthread_rwlock_unlock(&topo.flows_rwlock);

    // 申请重路由了，那
    pthread_mutex_lock(&fp->flow_mutex);
    fp->cnt = 0;
    fp->every_hop = false;
    fp->update_rule = true;
    pthread_mutex_unlock(&fp->flow_mutex);
}

/*
 * 监控线程，监控流的实际时延与理论时延变化情况
 * 每秒检测一次（会遍历所有流，流数量较多时，时间开销是个问题）
 * 超过理论时延一定比例（1.5），增加计数器。连续3s超过，就下发测量流表，
 * 发起动态路由请求。（问题在于）
 */
void *monitor_thread(void *f_)
{
    int err, real_delay;
    int delay_1, delay_2;
    sigset_t set;
    Flow *fp = nullptr;
    unordered_map<string, string> ump;

    sigaddset(&set, SIGALRM);
    err = sigprocmask(SIG_BLOCK, &set, NULL);
    if (err != 0)
    {
        spdlog::error("mointor thread 设置信号屏蔽失败");
        exit(EXIT_FAILURE);
    }

    while (topo.m_slot <= MAX_SLOT)
    {
        ump.clear();
        redis_cli.hgetall(FLOW_DELAY, std::inserter(ump, ump.begin()));
        pthread_rwlock_rdlock(&topo.flows_rwlock);
        for (auto &item : topo.flows)
        {
            auto &f = item.second;

            /* ump中无记录，说明该流的测量信息未写入到数据库，可能是没有发包 */
            if (ump.find(f.delay_key) == ump.end())
                continue;

            /* 理论时延为0，说明该流刚加入，还未下发流表 */
            if (f.theory_delay == 0)
                continue;

            real_delay = stoi(ump[f.delay_key]) / 1000; // 单位统一为ms
            // spdlog::info("flow: {}, 理论时延: {}, 测量时延: {}",
            //              f.id, f.theory_delay, real_delay);

            if (delay_1 == real_delay && real_delay != 0)
            {
                delay_2 = delay_1;
            }
            delay_1 = real_delay;

            if ((real_delay > f.theory_delay * 1.5) || (real_delay == delay_2 && real_delay == delay_1 && real_delay != 0))
            {
                spdlog::info("flow: {}, 理论时延: {}, 测量时延: {}",
                             f.id, f.theory_delay, real_delay);

                pthread_mutex_lock(&f.flow_mutex);
                ++f.cnt;
                if (f.cnt == 3)
                {
                    spdlog::info("flow: {} cnt = 3，进行逐跳测量", f.id);
                    f.every_hop = true;
                    f.update_rule = true;
                    fp = &f;
                    pthread_mutex_unlock(&f.flow_mutex);
                    break;
                }
                pthread_mutex_unlock(&f.flow_mutex);
            }
            else
            {
                pthread_mutex_lock(&f.flow_mutex);
                f.cnt = 0;
                f.every_hop = false;
                pthread_mutex_unlock(&f.flow_mutex);
            }
        }
        pthread_rwlock_unlock(&topo.flows_rwlock);

        if (fp != nullptr)
        {
            sleep(15); /* 等待下发流表，测量数据更新 */
            dynamic_routing(fp);
            sleep(15); /* 新路由下发后，要等新的测量信息写入 */
            for (auto &item : topo.flows)
                item.second.cnt = 0;
            fp = nullptr;
        }

        sleep(1);
    }

    return NULL;
}

void add_flows(void)
{
    string Beijin = "2001", Sanya = "2002";

    topo.read_flow(66);

    // topo.add_flow("1101", "1102", 2);
    // TGM 算法测试
    // topo.add_flow("1101", "1102", 2);
    // topo.add_flow("1101", "1103", 2);
    // topo.add_flow("1101", "1104", 2);
    // topo.add_flow("1101", "1105", 2);
    // topo.add_flow("1101", "1106", 2);
}

void sig_call(void)
{
    uint64_t end, start = get_time_ns();
    redis_cli.set(CURRENT_SLOT, topo.m_slice);
    for (auto &item : topo.flow_rules)
        pool.enqueue(item);
    end = get_time_ns();
    spdlog::info("当前时隙：{:5}，路由变化的业务数：{:5}，更新路由用时：{:7} us",
                 topo.m_slot, topo.flow_rules.size(), ((end - start) / 1000));

    if (topo.m_slot < MAX_SLOT)
    {
        topo.m_slot += 1;
    }
    topo.update_routing();
    sigflag = false;
}

void start_itimer(void)
{
    struct timespec ts;
    struct itimerval itimer;
    int64_t start_time, begin, end;
    string cmd;

    clock_gettime(CLOCK_REALTIME, &ts);
    start_time = ts.tv_sec + 40;
    spdlog::info("开始时间设定在 当前时间 + 40s");

    begin = get_time_ns();
    for (const string &node : topo.nodes)
    {
        cmd = fmt::format(CMD_RUN_IN_DOCKER, node, start_time);
        system(cmd.c_str());
    }
    end = get_time_ns();
    spdlog::info("在各Docker中启动维护拓扑的程序，用时: {} ms",
                 (end - begin) / MS_TO_NS);

    sleep(4);              /* 等待容器内部执行完第0s的添加端口 */
    topo.update_routing(); /* 根据第0秒的路由信息，生成流表信息 */
    sig_call();            /* 第0秒要下发所有流表，处理时间较长，单独处理 */

    signal(SIGALRM, execute_script); /* 注册信号的处理函数 */
    clock_gettime(CLOCK_REALTIME, &ts);
    if (ts.tv_sec > start_time)
    {
        spdlog::error("当前时间: {} 超过预定开始时间 {}，退出！\n",
                      ts.tv_sec, start_time);
        exit(EXIT_FAILURE);
    }
    itimer.it_interval.tv_sec = 1;
    itimer.it_interval.tv_usec = 0;
    itimer.it_value.tv_sec = start_time - ts.tv_sec - 1;
    itimer.it_value.tv_usec = 1000000ULL - ts.tv_nsec / 1000 + 50000;
    setitimer(ITIMER_REAL, &itimer, NULL);
}

int main()
{
    pthread_t interactive_tid; /* 交互线程 */
    pthread_t monitor_tid;     /* 监视线程 */
    redis_cli.auth(REDIS_PASSWD);

    /* 日志设置 */
    auto logger = spdlog::basic_logger_mt("logger", "./main.log", true);
    logger->set_level(spdlog::level::debug);
    logger->flush_on(spdlog::level::debug);
    logger->set_pattern("[%H:%M:%S.%F %l]: %v");
    spdlog::set_default_logger(logger);

    /* 启动docker，搭建网络拓扑 */
    topo.run();

    /* 添加流 */
    add_flows();

    /* 创建定时任务 */
    start_itimer();

    /* 创建交互线程，用于添加、删除业务 */
    pthread_create(&interactive_tid, NULL, interactive_thread, NULL);
    pthread_create(&monitor_tid, NULL, monitor_thread, NULL);

    while (topo.m_slot <= MAX_SLOT)
    {
        pause();
        if (sigflag)
        {
            sig_call();
        }
    }

    pthread_cancel(interactive_tid);
    pthread_cancel(monitor_tid);
    spdlog::info("仿真结束");
    return 0;
}
